`include "defines.v"

module btb(
  input clk,
  input rst_n,
  // 查询路径
  input [`VADDR_W-1:0]pc_i,
  output hit_o,
  output [`VADDR_W-1:0]predict_pc_o,
  // 更新路径
  input redict_i,
  input [1:0] type_i,
  input [`VADDR_W-1:0] btb_pc_i,
  input [`VADDR_W-1:0] btb_jump_pc_i
);
// wire [20:0] tag = pc_i[`VADDR_W-1:12];
wire [`BTB_TAG_W-1:0] tag = pc_i[`VADDR_W-1:(`VADDR_W-`BTB_TAG_W)];
wire [ 6:0] index = pc_i[`BTB_DEPTH :1];

wire [1:0] replace_way;
wire sram_wen [0:3];
wire [`BTB_DEPTH-1:0]   sram_raddr [0:3];
wire [`BTB_ENTRY_W-1:0] sram_dout  [0:3];
wire [`BTB_ENTRY_W-1:0] update_entry;

/* predict logic */
wire [0:3]  hit_vector;
wire [`VADDR_W-1-1:0] sram_hit_out ;
wire hit_replace;

assign sram_raddr[0] = index;
assign sram_raddr[1] = index;
assign sram_raddr[2] = index;
assign sram_raddr[3] = index;
// `BTB_ENTRY_W-2-`BTB_TAG_W+1
assign hit_vector[0] = (sram_dout[0][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W] == tag) && sram_dout[0][`BTB_ENTRY_W-1]; 
assign hit_vector[1] = (sram_dout[1][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W] == tag) && sram_dout[1][`BTB_ENTRY_W-1]; 
assign hit_vector[2] = (sram_dout[2][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W] == tag) && sram_dout[2][`BTB_ENTRY_W-1]; 
assign hit_vector[3] = (sram_dout[3][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W] == tag) && sram_dout[3][`BTB_ENTRY_W-1]; 
assign hit_replace   = (update_entry[`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W] == tag) && update_entry[`BTB_ENTRY_W-1]; 
assign hit_o = |hit_vector | hit_replace;

wire [`BTB_TAG_W-1:0] debug0_tag = sram_dout[0][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W];
wire [`BTB_TAG_W-1:0] debug1_tag = sram_dout[1][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W];
wire [`BTB_TAG_W-1:0] debug2_tag = sram_dout[2][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W];
wire [`BTB_TAG_W-1:0] debug3_tag = sram_dout[3][`BTB_ENTRY_W-2:`BTB_ENTRY_W-1-`BTB_TAG_W];

wire debug0_valid = sram_dout[0][`BTB_ENTRY_W-1];
wire debug1_valid = sram_dout[1][`BTB_ENTRY_W-1];
wire debug2_valid = sram_dout[2][`BTB_ENTRY_W-1];
wire debug3_valid = sram_dout[3][`BTB_ENTRY_W-1];

wire debug0_hit = hit_vector[0];
wire debug1_hit = hit_vector[1];
wire debug2_hit = hit_vector[2];
wire debug3_hit = hit_vector[3];

assign sram_hit_out = ({(`VADDR_W-1){hit_vector[0]}} & sram_dout[0][`VADDR_W:2]) | 
                      ({(`VADDR_W-1){hit_vector[1]}} & sram_dout[1][`VADDR_W:2]) | 
                      ({(`VADDR_W-1){hit_vector[2]}} & sram_dout[2][`VADDR_W:2]) | 
                      ({(`VADDR_W-1){hit_vector[2]}} & sram_dout[2][`VADDR_W:2]) | 
                      ({(`VADDR_W-1){hit_replace}}   & update_entry[`VADDR_W:2]) ;

assign predict_pc_o = {sram_hit_out , 1'b0};

/* update logic */

assign update_entry = {redict_i , btb_pc_i[`VADDR_W-1:(`VADDR_W-`BTB_TAG_W)] , btb_jump_pc_i[`VADDR_W-1:1],type_i};

reg [31:0] LFSR;
always@(posedge clk or negedge rst_n)
  if(~rst_n)
    LFSR <= 32'h1234_abcd;
  else
    LFSR <= {LFSR[0],LFSR[31:1]};

assign replace_way = LFSR[1:0];

genvar i;
generate 
  for(i=0;i<4;i=i+1)begin
    assign sram_wen[i] = (replace_way == i) && redict_i;
  end
endgenerate

generate 
  for(i=0;i<4;i=i+1)begin
    dpram #(
      .ADDR_WIDTH(`BTB_DEPTH) ,
      .DATA_WIDTH(`BTB_ENTRY_W)
    ) sram (
      .data       (update_entry ),
      .read_addr  (sram_raddr[i]),
      .write_addr (btb_pc_i[`BTB_DEPTH :1]),
      .we         (sram_wen[i]  ),
      .clk        (clk          ),
      .q          (sram_dout[i] )
    );
  end
endgenerate

endmodule 

// 一读一写
